package io.github.guojiaxing1995.easyJmeter.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.github.guojiaxing1995.easyJmeter.mapper.JFileMapper;
import io.github.guojiaxing1995.easyJmeter.model.JFileDO;
import io.github.guojiaxing1995.easyJmeter.module.file.FileProperties;
import io.github.guojiaxing1995.easyJmeter.service.JFileService;
import io.github.guojiaxing1995.easyJmeter.vo.CutFileVO;
import io.github.guojiaxing1995.easyJmeter.vo.JFileVO;
import io.github.talelin.autoconfigure.exception.FailedException;
import io.github.talelin.autoconfigure.exception.NotFoundException;
import io.minio.DownloadObjectArgs;
import io.minio.GetObjectArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import com.jcraft.jsch.*;

import java.io.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


@Slf4j
@Service
public class JFileServiceImpl_local implements JFileService {

    private final MinioClient minioClient;

    @Value("${minio.bucketName}")
    private String bucketName;

    @Value("${minio.endpoint}")
    private String endpoint;
    
    @Value("${file.csv.dat.zip.use-local-storage:true}")
    private boolean useCsvLocalStorage;
    
    // 允许配置本地存储的文件类型，默认包含所有支持的类型
    @Value("${file.local-storage-types:csv,dat,zip,jmx,jar}")
    private String localStorageTypes;

    @Value("${file.remote.server-ip:10.254.241.143}")
    private String remoteServerIp;

    @Value("${file.remote.username:root}")
    private String remoteUsername;

    @Value("${file.remote.password:$Aa123456}")
    private String remotePassword;

    @Value("${file.remote.port:22}")
    private int sshPort;

    @Value("${file.remote.assets-path:/app/easy-jmeter/final/assets}")
    private String remoteAssetsPath;

    @Autowired
    public JFileServiceImpl_local(MinioClient minioClient) {
        this.minioClient = minioClient;
    }

    @Autowired
    private FileProperties fileProperties;

    @Autowired
    private JFileMapper jFileMapper;

    private static final int BUFFER_SIZE = 32 * 1024 * 1024; // 32MB缓冲区
    private static final ExecutorService FILE_PROCESSOR = Executors.newFixedThreadPool(
            Math.max(64, Runtime.getRuntime().availableProcessors() * 16));

    /**
     * 检查文件是否应该使用本地存储
     * @param filename 文件名
     * @return 如果文件应该使用本地存储则返回true
     */
    private boolean shouldUseLocalStorage(String filename) {
        if (!useCsvLocalStorage) {
            return false;
        }

        if (filename == null || filename.isEmpty()) {
            return false;
        }

        String ext = StringUtils.getFilenameExtension(filename.toLowerCase());
        if (ext == null) {
            return false;
        }
        
        // 检查文件扩展名是否在配置的本地存储类型列表中
        String[] types = localStorageTypes.toLowerCase().split(",");
        for (String type : types) {
            if (ext.equals(type.trim())) {
                log.info("文件 {} 将使用本地存储", filename);
                return true;
            }
        }
        
        log.info("文件 {} 将使用MinIO存储", filename);
        return false;
    }

    /**
     * 创建基于日期的存储路径并返回相关路径信息
     * @param filename 原始文件名
     * @return 包含存储路径信息的Map，包括保存路径、格式化路径、文件URL等
     */
    private Map<String, String> createStoragePaths(String filename) {
        boolean useLocalStorage = shouldUseLocalStorage(filename);
        String storeDir = this.getStoreDir();
        String dateStr = new SimpleDateFormat("yyyy/MM/dd").format(new Date());
        String localPath = Paths.get(storeDir, dateStr).toString();
        
        // 确保目录存在
        File localDir = new File(localPath);
        if (!localDir.exists()) {
            localDir.mkdirs();
        }
        
        // 不再添加时间戳前缀
        String saveFileName = filename;
        String savePath = Paths.get(localPath, saveFileName).toString();
        
        Map<String, String> pathInfo = new HashMap<>();
        pathInfo.put("localPath", localPath);
        pathInfo.put("saveFileName", saveFileName);
        pathInfo.put("savePath", savePath);
        
        if (useLocalStorage) {
            // 使用本地存储
            String formattedPath = "/local/" + Paths.get(dateStr, saveFileName).toString();
            String fileUrl = "file://" + savePath;
            
        pathInfo.put("formattedPath", formattedPath);
        pathInfo.put("fileUrl", fileUrl);
            pathInfo.put("storageType", "local");
        } else {
            // 使用MinIO存储
            pathInfo.put("formattedPath", "/" + bucketName + "/" + saveFileName);
            pathInfo.put("fileUrl", endpoint + "/" + bucketName + "/" + saveFileName);
            pathInfo.put("storageType", "minio");
        }
        
        return pathInfo;
    }

     /**
     * 根据文件上传至本地或MinIO，并创建文件记录
     * @param file 文件
     * @return 文件视图对象
     */
    @Override
    public JFileVO createFile(MultipartFile file) {
        String name = file.getOriginalFilename();
        String ext = StringUtils.getFilenameExtension(name);
        Long size = file.getSize();
        String contentType = file.getContentType();
        
        // 记录开始时间
        long startTime = System.currentTimeMillis();
        log.info("开始处理文件: {}, 大小: {}MB", name, size/(1024*1024));
        
        try {
                JFileDO jFileDO = new JFileDO();
                jFileDO.setName(name);
                jFileDO.setSize(size);
                jFileDO.setType(contentType);
                
            Map<String, String> pathInfo = createStoragePaths(name);
            String storageType = pathInfo.get("storageType");
            
            if ("local".equals(storageType)) {
                // 本地存储
            file.transferTo(new java.io.File(pathInfo.get("savePath")));
                log.info("文件已保存到本地: {}", pathInfo.get("savePath"));
            } else {
                // MinIO存储
                minioClient.putObject(PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(pathInfo.get("saveFileName"))
                    .stream(file.getInputStream(), file.getSize(), -1)
                    .contentType(contentType)
                    .build());
                log.info("文件已上传至MinIO: {}", pathInfo.get("saveFileName"));
            }
            
            jFileDO.setPath(pathInfo.get("formattedPath"));
            jFileDO.setUrl(pathInfo.get("fileUrl"));
                    jFileDO.setCut(false);
                
                jFileMapper.insert(jFileDO);
            jFileDO = jFileMapper.selectById(jFileDO.getId());
                
            // 记录处理时间和性能指标
            long endTime = System.currentTimeMillis();
            long totalTime = endTime - startTime;
            double speedMBs = size / (1024.0 * 1024.0) / (totalTime / 1000.0);
            
            log.info("文件处理完成: {}, 大小: {}MB, 存储类型: {}, 耗时: {}ms, 速度: {}MB/s",
                   name, size/(1024*1024), storageType, totalTime, speedMBs);
            
            return new JFileVO(jFileDO, formatSize(size));
                        } catch (Exception e) {
            log.error("文件处理失败:" + e.getMessage(), e);
            throw new FailedException(10190, e.getMessage());
        }
    }

    @Override
    public List<JFileVO> batchCreateFiles(List<MultipartFile> files) {
        if (files.isEmpty()) {
            return new ArrayList<>();
        }
        
        long startTime = System.currentTimeMillis();
        int totalFileCount = files.size();
        log.info("开始批量处理 {} 个文件", totalFileCount);
        
        // 创建足够大的线程池
        int poolSize = Math.min(totalFileCount, Runtime.getRuntime().availableProcessors() * 4);
        ExecutorService executor = Executors.newFixedThreadPool(poolSize);
        
        try {
            // 并行处理所有文件
            List<CompletableFuture<JFileVO>> futures = files.stream()
                .map(file -> CompletableFuture.supplyAsync(() -> {
                    try {
                        return createFile(file);
                    } catch (Exception e) {
                        log.error("处理文件失败: {}, 错误: {}", file.getOriginalFilename(), e.getMessage());
                        return null;
                    }
                }, executor))
                .collect(java.util.stream.Collectors.toList());
            
            // 等待所有文件处理完成并收集结果
            List<JFileVO> results = futures.stream()
                .map(CompletableFuture::join)
                .filter(Objects::nonNull)
                .collect(java.util.stream.Collectors.toList());
            
            long endTime = System.currentTimeMillis();
            log.info("批量处理完成: 总文件数={}, 成功数={}, 总耗时={}秒",
                    totalFileCount, results.size(), (endTime - startTime)/1000.0);
            
            return results;
        } finally {
            executor.shutdown();
        }
    }

    @Override
    public Boolean setFileCut(Integer id, Boolean cut) {
        JFileDO jFileDO = jFileMapper.selectById(id);
        if (jFileDO == null){
            throw new NotFoundException(12103);
        }
        
        // 修复空指针问题 - 确保cut值不为空
        if (cut == null) {
            cut = false; // 如果传入null，则默认设为false
        }
        
        jFileDO.setCut(cut);
        int result = jFileMapper.updateById(jFileDO);
        
        // 增加日志监控切分状态
        log.info("设置文件切分状态: 文件ID={}, 名称={}, 切分状态={}, 更新结果={}", 
                id, jFileDO.getName(), cut, result > 0);
        
        return result > 0;
    }

    @Override
    public String downloadFile(Integer id, String dir){
        JFileDO jFileDO = jFileMapper.selectById(id);
        if (jFileDO == null) {
            log.error("文件记录不存在，ID: {}", id);
            throw new NotFoundException(12103, "文件记录不存在");
        }
        
        String filePath;
        if (dir == null) {
            String storeDir = this.getStoreDir();
            filePath = Paths.get(storeDir, String.valueOf(Instant.now().toEpochMilli()) + jFileDO.getName()).toString();
        } else {
            java.io.File file = new File(dir);
            if (!file.exists()) {
                file.mkdirs();
            }
            filePath = Paths.get(dir, jFileDO.getName()).toString();
        }

        // 判断存储类型
        try {
        if (jFileDO.getPath().startsWith("/local/")) {
                // 本地存储的文件
                log.info("从服务端存储下载文件: {}", jFileDO.getName());
                return downloadLocalFile(jFileDO, filePath);
            } else {
                // MinIO存储的文件
                log.info("从MinIO下载文件: {}", jFileDO.getName());
                return downloadMinioFile(jFileDO, filePath);
            }
        } catch (Exception e) {
            log.error("文件下载异常: {}", e.getMessage(), e);
            throw new RuntimeException("文件下载失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 从本地存储下载文件 - 提供两种方式：Web页面直接下载和SCP获取
     */
    private String downloadLocalFile(JFileDO jFileDO, String targetFilePath) throws IOException {
        log.info("准备下载本地存储文件: {}", jFileDO.getName());
        
        // 从文件路径中提取相对路径
        String relativePath = jFileDO.getPath().replace("/local/", "");
        
        // 如果是Web请求（目标路径包含临时目录），尝试使用SCP
        if (targetFilePath.contains("temp") || targetFilePath.contains("tmp")) {
            log.info("通过SCP获取服务器文件");
            // 使用配置的远程资产路径而不是硬编码路径
            String remoteFilePath = remoteAssetsPath + "/" + relativePath;
            // 确保路径格式正确（避免双斜杠）
            remoteFilePath = remoteFilePath.replaceAll("//+", "/");
            
            File destFile = new File(targetFilePath);
            
            // 确保目标目录存在
            destFile.getParentFile().mkdirs();
            
            log.info("准备通过SCP从服务器{}下载文件: {}", remoteServerIp, remoteFilePath);
            
            boolean downloadSuccess = downloadFileWithScp(remoteFilePath, targetFilePath);
            if (downloadSuccess) {
                log.info("成功通过SCP下载文件到: {}", targetFilePath);
                return targetFilePath;
            } else {
                log.error("通过SCP下载文件失败: {}", remoteFilePath);
                throw new NotFoundException(12103, "无法从服务器获取文件: " + jFileDO.getName());
            }
        } else {
            // 对于Web浏览器下载，直接返回文件URL
            log.info("Web请求下载，返回文件URL");
            // 生成一个可访问的HTTP URL
            return jFileDO.getUrl();
        }
    }
    
    /**
     * 使用SCP从远程服务器下载文件
     * @param remoteFilePath 远程服务器上的文件路径
     * @param localFilePath 保存到本地的文件路径
     * @return 下载是否成功
     */
    private boolean downloadFileWithScp(String remoteFilePath, String localFilePath) {
        JSch jsch = new JSch();
        Session session = null;
        ChannelExec channel = null;
        
        try {
            log.info("开始SCP下载，远程路径: {}, 本地路径: {}", remoteFilePath, localFilePath);
            
            // 设置SSH连接
            session = jsch.getSession(remoteUsername, remoteServerIp, sshPort);
            
            // 使用密码认证
            session.setPassword(remotePassword);
            log.info("使用密码认证");
            
            // 不严格检查主机密钥
            Properties config = new Properties();
            config.put("StrictHostKeyChecking", "no");
            session.setConfig(config);
            
            session.connect(30000); // 30秒超时
            log.info("SSH会话已连接到服务器: {}", remoteServerIp);
            
            // 设置执行SCP命令的通道
            String command = "scp -f " + remoteFilePath;
            channel = (ChannelExec) session.openChannel("exec");
            channel.setCommand(command);
            
            // 获取输入输出流
            OutputStream out = channel.getOutputStream();
            InputStream in = channel.getInputStream();
            
            channel.connect();
            log.info("SCP通道已连接");
            
            // 发送协议确认信号
            out.write(0);
            out.flush();
            
            // 准备接收文件
            File localFile = new File(localFilePath);
            
            try (FileOutputStream fos = new FileOutputStream(localFile)) {
                byte[] buf = new byte[BUFFER_SIZE];
                
                // 解析SCP协议响应
                int c = checkAck(in);
                if (c != 'C') {
                    log.error("未收到文件头信息，响应码: {}", c);
                    return false;
                }
                
                // 读取文件权限信息，忽略
                in.read(buf, 0, 5);
                
                // 读取文件大小
                long fileSize = 0;
                while (true) {
                    if (in.read(buf, 0, 1) < 0) {
                        break;
                    }
                    if (buf[0] == ' ') break;
                    fileSize = fileSize * 10 + (buf[0] - '0');
                }
                log.info("文件大小: {}KB", fileSize/1024);
                
                // 读取文件名，直到换行符
                StringBuilder fileName = new StringBuilder();
                while (true) {
                    in.read(buf, 0, 1);
                    if (buf[0] == (byte) 0x0a) break;
                    fileName.append((char) buf[0]);
                }
                log.info("远程文件名: {}", fileName.toString());
                
                // 发送确认，准备接收文件内容
                out.write(0);
                out.flush();
                
                // 接收文件内容
                long receivedBytes = 0;
                long startTime = System.currentTimeMillis();
                while (receivedBytes < fileSize) {
                    int length = in.read(buf, 0, (int) Math.min(buf.length, fileSize - receivedBytes));
                    if (length < 0) break;
                    fos.write(buf, 0, length);
                    receivedBytes += length;
                    
                    // 每10MB记录一次进度
                    if (receivedBytes % (10*1024*1024) == 0) {
                        long elapsedTime = System.currentTimeMillis() - startTime;
                        double speed = (receivedBytes / 1024.0 / 1024.0) / (elapsedTime / 1000.0);
                        log.info("已下载: {}MB, 进度: {}%, 速度: {}MB/s", 
                            receivedBytes/(1024*1024), 
                            Math.round((receivedBytes * 100.0) / fileSize),
                            speed);
                    }
                }
                
                // 最终进度
                long elapsedTime = System.currentTimeMillis() - startTime;
                double speed = (receivedBytes / 1024.0 / 1024.0) / (Math.max(elapsedTime, 1) / 1000.0);
                log.info("下载完成: {}MB, 耗时: {}秒, 平均速度: {}MB/s", 
                        receivedBytes/(1024*1024), 
                        elapsedTime/1000,
                        speed);
                
                // 检查确认码
                if (checkAck(in) != 0) {
                    log.error("文件接收完成后未收到确认信号");
                    return false;
                }
                
                // 发送确认完成
                out.write(0);
                out.flush();
                
                // 验证文件是否成功下载
                if (localFile.exists() && localFile.length() == fileSize) {
                    log.info("文件成功下载到: {}", localFilePath);
                    return true;
                } else {
                    log.error("文件下载不完整，期望大小: {}, 实际大小: {}", fileSize, localFile.length());
                    return false;
                }
            }
        } catch (JSchException e) {
            log.error("SSH连接错误: {}", e.getMessage(), e);
            return false;
        } catch (IOException e) {
            log.error("I/O错误: {}", e.getMessage(), e);
            return false;
            } catch (Exception e) {
            log.error("SCP下载过程中发生错误: {}", e.getMessage(), e);
            return false;
        } finally {
            // 关闭连接
            if (channel != null) channel.disconnect();
            if (session != null) session.disconnect();
            log.info("SCP连接已关闭");
        }
    }

    /**
     * 检查SCP协议确认码
     * @param in 输入流
     * @return 确认码
     */
    private int checkAck(InputStream in) throws IOException {
        int b = in.read();
        
        // 0 表示成功，1表示警告，2表示错误，-1表示EOF
        if (b == 0) return b; // 成功
        if (b == -1) return b; // EOF
        
        if (b == 1 || b == 2) {
            StringBuilder sb = new StringBuilder();
            int c;
            do {
                c = in.read();
                sb.append((char)c);
            } while (c != '\n');
            
            if (b == 1) {
                log.warn("SCP警告: {}", sb.toString());
        } else {
                log.error("SCP错误: {}", sb.toString());
            }
        }
        return b;
    }

    /**
     * 从MinIO存储下载文件 - 修改为在MinIO下载失败时尝试SCP
     */
    private String downloadMinioFile(JFileDO jFileDO, String targetFilePath) {
        try {
            String bucket = jFileDO.getPath().split("/")[1];
            String name = jFileDO.getPath().split("/")[2];
            
            minioClient.downloadObject(DownloadObjectArgs.builder()
                .bucket(bucket)
                .object(name)
                .filename(targetFilePath)
                .build());
            
            log.info("从MinIO下载文件成功: {} -> {}", jFileDO.getName(), targetFilePath);
            return targetFilePath;
        } catch (Exception e) {
            log.warn("从MinIO下载文件失败，尝试通过SCP获取: {}", e.getMessage());
            
            try {
                // 使用配置的远程资产路径而不是硬编码路径
                String remoteFilePath = remoteAssetsPath + "/" + jFileDO.getName();
                // 确保路径格式正确（避免双斜杠）
                remoteFilePath = remoteFilePath.replaceAll("//+", "/");
                
                log.info("尝试通过SCP从服务器获取文件: {}", remoteFilePath);
                
                boolean downloadSuccess = downloadFileWithScp(remoteFilePath, targetFilePath);
                if (downloadSuccess) {
                    log.info("通过SCP成功下载文件: {}", targetFilePath);
                    return targetFilePath;
                }
            } catch (Exception ex) {
                log.error("SCP下载也失败: {}", ex.getMessage());
            }
            
            throw new RuntimeException("无法下载文件: " + e.getMessage(), e);
        }
    }

    private boolean isFileValid(File file, String filename) {
        if (!file.exists() || !file.isFile()) {
            log.error("文件不存在或不是一个有效文件: {}", file.getAbsolutePath());
            return false;
        }
        
        try (FileInputStream fis = new FileInputStream(file)) {
            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead = fis.read(buffer);
            return bytesRead != -1;
        } catch (IOException e) {
            log.error("验证文件有效性失败: {}, 错误: {}", file.getAbsolutePath(), e.getMessage());
            return false;
        }
    }

    private File[] findFiles(File directory, String filename) {
        List<File> files = new ArrayList<>();
        findFilesRecursive(directory, filename, files);
        return files.toArray(new File[0]);
    }

    private void findFilesRecursive(File directory, String filename, List<File> files) {
        File[] children = directory.listFiles();
        if (children != null) {
            for (File child : children) {
                if (child.isDirectory()) {
                    findFilesRecursive(child, filename, files);
                } else if (child.getName().equals(filename)) {
                    files.add(child);
                }
            }
        }
    }

    @Override
    public void downloadCutFile(List<CutFileVO> cutFileVOList, String dir) {
        java.io.File file = new File(dir);
        if (!file.exists()) {
            file.mkdirs();
        }
        for (CutFileVO cutFileVO : cutFileVOList) {
            JFileDO jFileDO = jFileMapper.selectById(cutFileVO.getId());
            String filePath = Paths.get(dir, cutFileVO.getOriginName()).toString();
            
            // 检查是本地存储还是MinIO存储
            if (jFileDO.getPath().startsWith("/local/")) {
                // 本地存储的文件直接复制
                String localFilePath = jFileDO.getUrl().replace("file://", "");
                try {
                    // 创建源文件和目标文件对象
                    File sourceFile = new File(localFilePath);
                    File destFile = new File(filePath);
                    
                    // 检查源文件是否存在
                    if (!sourceFile.exists()) {
                        log.error("本地源文件不存在: {}", localFilePath);
                        continue;
                    }
                    
                    // 复制文件
                    try (FileInputStream fis = new FileInputStream(sourceFile);
                         FileOutputStream fos = new FileOutputStream(destFile)) {
                        byte[] buffer = new byte[BUFFER_SIZE];
                        int bytesRead;
                        while ((bytesRead = fis.read(buffer)) != -1) {
                            fos.write(buffer, 0, bytesRead);
                        }
                    }
                    log.info("从本地存储复制切分文件: {} -> {}", jFileDO.getName(), filePath);
                } catch (Exception e) {
                    log.error("本地文件复制异常:" + e);
                    throw new RuntimeException(e);
                }
            } else {
                // MinIO存储的文件下载
            String bucket = jFileDO.getPath().split("/")[1];
            String name = jFileDO.getPath().split("/")[2];
            try {
                minioClient.downloadObject(DownloadObjectArgs.builder().bucket(bucket).object(name).filename(filePath).build());
                    log.info("从MinIO下载切分文件: {} -> {}", jFileDO.getName(), filePath);
            } catch (Exception e) {
                log.error("文件下载异常:" + e);
                throw new RuntimeException(e);
                }
            }
        }
    }

    @Override
    public List<JFileDO> createCsvCutFiles(Map<Integer, List<String>> fileMap) {
        Map.Entry<Integer, List<String>> entry = fileMap.entrySet().iterator().next();
        // 切分文件id
        Integer fid = entry.getKey();
        List<String> filePath = entry.getValue();
        List<JFileDO> jFileDOS = new ArrayList<>();
        
        // 检查原始文件是否是本地存储
        JFileDO originalFile = jFileMapper.selectById(fid);
        boolean isLocalStorage = originalFile != null && originalFile.getPath().startsWith("/local/");
        
        for (String path: filePath) {
            File file = new File(path);
            
            if (isLocalStorage) {
                // 本地存储 - 将文件复制到存储目录
                try {
                    // 使用辅助方法创建存储路径
                    Map<String, String> pathInfo = createStoragePaths(file.getName());
                    
                    // 复制文件
                    try (FileInputStream fis = new FileInputStream(file);
                         FileOutputStream fos = new FileOutputStream(pathInfo.get("savePath"))) {
                        byte[] buffer = new byte[BUFFER_SIZE];
                        int bytesRead;
                        while ((bytesRead = fis.read(buffer)) != -1) {
                            fos.write(buffer, 0, bytesRead);
                        }
                    }
                    
                    // 创建数据库记录
                    JFileDO cutfile = new JFileDO();
                    cutfile.setName(file.getName());
                    cutfile.setPath(pathInfo.get("formattedPath"));
                    cutfile.setUrl(pathInfo.get("fileUrl"));
                    cutfile.setSize(file.length());
                    cutfile.setType(getContentType(file.getName()));
                    cutfile.setOriginId(fid);
                    jFileMapper.insert(cutfile);
                    JFileDO fileDO = jFileMapper.selectById(cutfile.getId());
                    jFileDOS.add(fileDO);
                    
                    log.info("切分文件已保存到本地: {}", pathInfo.get("savePath"));
            } catch (Exception e) {
                    log.error("保存切分文件到本地异常: " + e.getMessage(), e);
                }
            } else {
                // MinIO存储 - 保持原有逻辑
                String timestamp = String.valueOf(Instant.now().toEpochMilli());
                String fileName = timestamp + "_" + file.getName();
                try {
                    minioClient.putObject(PutObjectArgs.builder()
                        .bucket(bucketName)
                        .object(fileName)
                        .stream(new FileInputStream(file), file.length(), -1)
                        .build());
                    
                    log.info("切分文件已上传至MinIO: {}", fileName);
                
            JFileDO cutfile = new JFileDO();
            cutfile.setName(file.getName());
            cutfile.setPath("/" + bucketName + "/" + fileName);
            cutfile.setUrl(endpoint + "/" + bucketName + "/" + fileName);
            cutfile.setSize(file.length());
                    cutfile.setType(getContentType(file.getName()));
            cutfile.setOriginId(fid);
            jFileMapper.insert(cutfile);
            JFileDO fileDO = jFileMapper.selectById(cutfile.getId());
            jFileDOS.add(fileDO);
                } catch (Exception e) {
                    log.error("上传切分文件异常:" + e);
                }
            }
        }
        return jFileDOS;
    }

    @Override
    public JFileDO searchById(Integer id) {
        return jFileMapper.selectById(id);
    }

    @Override
    public Boolean needCut(String[] fileIds) {
        for (String id : fileIds) {
            JFileDO jFileDO = jFileMapper.selectById(Integer.valueOf(id));
            if (jFileDO.getCut()) {
                return true;
            }
        }
        return false;
    }

    @Override
    public JFileDO createFile(String filePath) {
        long startTime = System.currentTimeMillis();
        File file = new File(filePath);
        if (!file.exists() || !file.isFile()) {
            log.error("文件不存在或不是一个有效文件: {}", filePath);
            return null;
        }
        
        String name = file.getName();
        
        try {
            log.info("开始保存文件: {}, 大小: {}MB", name, file.length()/(1024*1024));
            
            boolean useLocalStorage = shouldUseLocalStorage(name);
            Map<String, String> pathInfo = createStoragePaths(name);
            String storageType = pathInfo.get("storageType");
            
            if (useLocalStorage) {
                // 使用本地存储
            try (FileInputStream fis = new FileInputStream(file);
                 FileOutputStream fos = new FileOutputStream(pathInfo.get("savePath"))) {
                byte[] buffer = new byte[BUFFER_SIZE];
                int bytesRead;
                while ((bytesRead = fis.read(buffer)) != -1) {
                    fos.write(buffer, 0, bytesRead);
                }
                }
                log.info("文件已保存到本地: {}", pathInfo.get("savePath"));
            } else {
                // 使用MinIO存储
                try (FileInputStream fis = new FileInputStream(file)) {
                    minioClient.putObject(PutObjectArgs.builder()
                        .bucket(bucketName)
                        .object(pathInfo.get("saveFileName"))
                        .stream(fis, file.length(), -1)
                        .contentType(getContentType(name))
                        .build());
                }
                log.info("文件已上传至MinIO: {}", pathInfo.get("saveFileName"));
            }
            
            // 创建数据库记录
            JFileDO jFileDO = new JFileDO();
            jFileDO.setName(name);
            jFileDO.setPath(pathInfo.get("formattedPath"));
            jFileDO.setUrl(pathInfo.get("fileUrl"));
            jFileDO.setSize(file.length());
            jFileDO.setType(getContentType(name));
                jFileDO.setCut(false); // 默认不切分
            
            jFileMapper.insert(jFileDO);
            
            // 记录结束时间和性能指标
            long endTime = System.currentTimeMillis();
            long totalTime = endTime - startTime;
            double speedMBs = file.length() / (1024.0 * 1024.0) / (totalTime / 1000.0);
            
            log.info("文件保存成功: {}, 大小: {}MB, 存储类型: {}, 耗时: {}ms, 速度: {}MB/s",
                   name, file.length()/(1024*1024), storageType, totalTime, speedMBs);
            
            return jFileDO;
        } catch (Exception e) {
            log.error("从本地文件保存失败: {}, 错误: {}", filePath, e.getMessage(), e);
            return null;
        }
    }

    @Override
    public Boolean updateById(JFileDO jFileDO) {
        return jFileMapper.updateById(jFileDO) > 0;
    }

    @Override
    public void downLoadJmeterLogZip(String taskId, OutputStream outputStream) throws IOException {
        QueryWrapper<JFileDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("task_id", taskId).eq("type", "log");
        List<JFileDO> jFileDOS = jFileMapper.selectList(queryWrapper);
        try (ZipArchiveOutputStream zipOut = new ZipArchiveOutputStream(outputStream)) {
            for (JFileDO fileDO : jFileDOS){
                String fileName = new File(fileDO.getPath()).getName();
                log.info(fileName);
                try {
                    InputStream stream = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
                    ZipArchiveEntry entry = new ZipArchiveEntry(fileName);
                    zipOut.putArchiveEntry(entry);
                    byte[] buffer = new byte[BUFFER_SIZE];
                    int bytesRead;
                    while ((bytesRead = stream.read(buffer)) != -1) {
                        zipOut.write(buffer, 0, bytesRead);
                    }
                    zipOut.closeArchiveEntry();
                } catch (Exception e) {
                    log.error("文件上传异常:" + e);
                    throw new RuntimeException(e);
                }
            }
            zipOut.finish();
        }
    }

    @Override
    public List<JFileDO> searchJtlByTaskId(String taskId) {
        QueryWrapper<JFileDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("task_id", taskId);
        queryWrapper.like("name", ".jtl");
        return jFileMapper.selectList(queryWrapper);
    }

    @Override
    public List<JFileDO> searchStatsByTaskId(String taskId) {
        QueryWrapper<JFileDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("task_id", taskId);
        queryWrapper.like("name", "stats");
        return jFileMapper.selectList(queryWrapper);
    }

    @Override
    public String getStoreDir() {
        Path path = Paths.get(fileProperties.getStoreDir()).toAbsolutePath();
        java.io.File file = new File(path.toString());
        if (!file.exists()) {
            file.mkdirs();
        }
        return path.toString();
    }

     /**
     * 获取文件Content-Type
     */
    private String getContentType(String filename) {
        if (filename == null) {
            return "application/octet-stream";
        }
        
        // 处理特殊文件名，如包含非法字符的文件名
        try {
            filename = filename.replaceAll("[\\\\/:*?\"<>|]", "_");
        } catch (Exception e) {
            log.warn("处理文件名时出错: {}", e.getMessage());
        }
        
        Map<String, String> mimeMap = new HashMap<>();
        mimeMap.put("csv", "text/csv");
        mimeMap.put("json", "application/json");
        mimeMap.put("txt", "text/plain");
        mimeMap.put("xml", "text/xml");
        mimeMap.put("jmx", "application/xml");
        mimeMap.put("jtl", "application/xml");
        mimeMap.put("log", "text/plain");
        mimeMap.put("jar", "application/java-archive");
        mimeMap.put("dat", "application/octet-stream");
        mimeMap.put("data", "application/octet-stream");
        mimeMap.put("bin", "application/octet-stream");
        mimeMap.put("zip", "application/zip");
        
        String extension = org.springframework.util.StringUtils.getFilenameExtension(filename);
        if (extension != null && mimeMap.containsKey(extension.toLowerCase())) {
            return mimeMap.get(extension.toLowerCase());
        }
        
        return "application/octet-stream";
    }

     /**
     * 格式化文件大小为人类可读格式
     */
    private String formatSize(long size) {
        if (size/(1024 * 1024 * 1024) >= 1) {
            return String.format("%.2f GB", size/(float)(1024 * 1024 * 1024));
        } else if (size/(1024 * 1024) >= 1) {
            return String.format("%.2f MB", size/(float)(1024 * 1024));
        } else if (size/1024 >= 1) {
            return String.format("%.2f KB", size/(float)1024);
        } else {
            return size + " B";
        }
    }

     /**
     * 从URL中提取真实的文件路径
     */
    private String extractPathFromUrl(String url) {
        if (url == null || url.isEmpty()) {
            return null;
        }
        
        try {
            // 处理Linux格式路径
            if (url.startsWith("file:///")) {
                return url.substring(7); // 去掉file:///
            }
            // 处理Windows格式路径
            else if (url.startsWith("file://")) {
                return url.substring(5); // 去掉file://
                    }
                } catch (Exception e) {
            log.error("解析URL失败: {}", url, e);
        }
        
        return url; // 返回原始URL
    }
}

